#pragma once

#include<iostream>
#include<queue>
#include<pthread.h>
#include"Task.hpp"
#include<unistd.h>

#define NUM 5
using namespace std;

template<class T>
class ThreadPool
{
private:
    bool IsEmpty()
    {
        return _task_queue.size() == 0;
    }

    void Queue_Lock()
    {
        pthread_mutex_lock(&_mutex);
    }

    void Queue_Unlock()
    {
        pthread_mutex_unlock(&_mutex);
    }

    void Wait()
    {
        pthread_cond_wait(&_cond, &_mutex);
    }

    void WaitUp()
    {
        pthread_cond_signal(&_cond);
    }


public:
    ThreadPool(int num = NUM)
        :_task_num(num)
    {
        pthread_mutex_init(&_mutex, nullptr);
        pthread_cond_init(&_cond, nullptr);
    }

    ~ThreadPool()
    {
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_cond);
    }

    static void* Rountine(void* arg)
    {
        pthread_detach(pthread_self());
        ThreadPool* self = (ThreadPool*)arg;
        while(true)
        {
            self->Queue_Lock();
            while(self->IsEmpty())
            {
                self->Wait();
            }
            T task;
            self->Pop(task);

            self->Queue_Unlock();
            task.Run();
        }
    }

    void ThreadPool_init()
    {
        pthread_t tid;
        for(int i = 0; i < _task_num; i++)
        {
            pthread_create(&tid, nullptr, Rountine, this);
        }
    }

    void Push(const T& data)
    {
        Queue_Lock();
        _task_queue.push(data);
        Queue_Unlock();
        WaitUp();
    }

    void Pop(T& task)
    {
        task = _task_queue.front();
        _task_queue.pop();
    }
private:
    queue<T> _task_queue;
    int _task_num;
    pthread_mutex_t _mutex;
    pthread_cond_t _cond;
};